﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;

namespace CounterStrikeSourceAimbot
{

    public struct CSPoint
    {
        public double X, Y, Z;
    }

    public class PlayerData : INotifyPropertyChanged
    {
        public PlayerData()
        {
            this.health = -1;
            this.team = -1;
        }

        public DateTime LastPositionSet;

        #region INotifyPropertyChanged members and helpers

        public event PropertyChangedEventHandler PropertyChanged;

        private static bool AreEqualObjects(object obj1, object obj2)
        {
            var obj1Null = ReferenceEquals(obj1, null);
            var obj2Null = ReferenceEquals(obj2, null);

            if (obj1Null && obj2Null)
                return true;

            if (obj1Null || obj2Null)
                return false;

            if (obj1.GetType() != obj2.GetType())
                return false;

            if (ReferenceEquals(obj1, obj2))
                return true;

            return obj1.Equals(obj2);
        }

        private void OnPropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                return;

            if (this.PropertyChanged != null)
                foreach (var propertyName in propertyNames)
                    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region X Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double X
        {
            get { return x; }
            set
            {
                if (AreEqualObjects(x, value))
                    return;

                var _fieldOldValue = x;

                x = value;

                PlayerData.OnXChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("X");
            }
        }

        private double x;

        public EventHandler<PropertyValueChangedEventArgs<double>> XChanged;

        public static void OnXChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.XChanged != null)
                obj.XChanged(obj, e);

            obj.LastPositionSet = DateTime.Now;
        }

        #endregion

        #region Y Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Y
        {
            get { return y; }
            set
            {
                if (AreEqualObjects(y, value))
                    return;

                var _fieldOldValue = y;

                y = value;

                PlayerData.OnYChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Y");
            }
        }

        private double y;

        public EventHandler<PropertyValueChangedEventArgs<double>> YChanged;

        public static void OnYChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.YChanged != null)
                obj.YChanged(obj, e);

            obj.LastPositionSet = DateTime.Now;
        }

        #endregion

        #region Z Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Z
        {
            get { return z; }
            set
            {
                if (AreEqualObjects(z, value))
                    return;

                var _fieldOldValue = z;

                z = value;

                PlayerData.OnZChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Z");
            }
        }

        private double z;

        public EventHandler<PropertyValueChangedEventArgs<double>> ZChanged;

        public static void OnZChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.ZChanged != null)
                obj.ZChanged(obj, e);

            obj.LastPositionSet = DateTime.Now;
        }

        #endregion

        #region Health Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Health
        {
            get { return health; }
            set
            {
                if (AreEqualObjects(health, value))
                    return;

                var _fieldOldValue = health;

                health = value;

                PlayerData.OnHealthChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Health");
            }
        }

        private double health;

        public EventHandler<PropertyValueChangedEventArgs<double>> HealthChanged;

        public static void OnHealthChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.HealthChanged != null)
                obj.HealthChanged(obj, e);

            obj.IsDead = Math.Abs(e.NewValue) < 0.01;
        }

        #endregion

        #region Pitch Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Pitch
        {
            get { return pitch; }
            set
            {
                if (AreEqualObjects(pitch, value))
                    return;

                var _fieldOldValue = pitch;

                pitch = value;

                PlayerData.OnPitchChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Pitch");
            }
        }

        private double pitch;

        public EventHandler<PropertyValueChangedEventArgs<double>> PitchChanged;

        public static void OnPitchChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.PitchChanged != null)
                obj.PitchChanged(obj, e);


        }

        #endregion

        #region Yaw Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Yaw
        {
            get { return yaw; }
            set
            {
                if (AreEqualObjects(yaw, value))
                    return;

                var _fieldOldValue = yaw;

                yaw = value;

                PlayerData.OnYawChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Yaw");
            }
        }

        private double yaw;

        public EventHandler<PropertyValueChangedEventArgs<double>> YawChanged;

        public static void OnYawChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.YawChanged != null)
                obj.YawChanged(obj, e);


        }

        #endregion

        #region Roll Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Roll
        {
            get { return roll; }
            set
            {
                if (AreEqualObjects(roll, value))
                    return;

                var _fieldOldValue = roll;

                roll = value;

                PlayerData.OnRollChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Roll");
            }
        }

        private double roll;

        public EventHandler<PropertyValueChangedEventArgs<double>> RollChanged;

        public static void OnRollChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.RollChanged != null)
                obj.RollChanged(obj, e);


        }

        #endregion

        #region Team Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public int Team
        {
            get { return team; }
            set
            {
                if (AreEqualObjects(team, value))
                    return;

                var _fieldOldValue = team;

                team = value;

                PlayerData.OnTeamChanged(this, new PropertyValueChangedEventArgs<int>(_fieldOldValue, value));

                this.OnPropertyChanged("Team");
            }
        }

        private int team;

        public EventHandler<PropertyValueChangedEventArgs<int>> TeamChanged;

        public static void OnTeamChanged(object sender, PropertyValueChangedEventArgs<int> e)
        {
            var obj = sender as PlayerData;

            if (obj.TeamChanged != null)
                obj.TeamChanged(obj, e);

            obj.ResetScale();
        }

        #endregion

        #region Name Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public string Name
        {
            get { return name; }
            set
            {
                if (AreEqualObjects(name, value))
                    return;

                var _fieldOldValue = name;

                name = value;

                PlayerData.OnNameChanged(this, new PropertyValueChangedEventArgs<string>(_fieldOldValue, value));

                this.OnPropertyChanged("Name");
            }
        }

        private string name;

        public EventHandler<PropertyValueChangedEventArgs<string>> NameChanged;

        public static void OnNameChanged(object sender, PropertyValueChangedEventArgs<string> e)
        {
            var obj = sender as PlayerData;

            if (obj.NameChanged != null)
                obj.NameChanged(obj, e);


        }

        #endregion

        #region IsDead Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public bool IsDead
        {
            get { return isDead; }
            set
            {
                if (AreEqualObjects(isDead, value))
                    return;

                var _fieldOldValue = isDead;

                isDead = value;

                PlayerData.OnIsDeadChanged(this, new PropertyValueChangedEventArgs<bool>(_fieldOldValue, value));

                this.OnPropertyChanged("IsDead");
            }
        }

        private bool isDead;

        public EventHandler<PropertyValueChangedEventArgs<bool>> IsDeadChanged;

        public static void OnIsDeadChanged(object sender, PropertyValueChangedEventArgs<bool> e)
        {
            var obj = sender as PlayerData;

            if (obj.IsDeadChanged != null)
                obj.IsDeadChanged(obj, e);

            
        }

        #endregion

        #region IsMainPlayer Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public bool IsMainPlayer
        {
            get { return isMainPlayer; }
            set
            {
                if (AreEqualObjects(isMainPlayer, value))
                    return;

                var _fieldOldValue = isMainPlayer;

                isMainPlayer = value;

                PlayerData.OnIsMainPlayerChanged(this, new PropertyValueChangedEventArgs<bool>(_fieldOldValue, value));

                this.OnPropertyChanged("IsMainPlayer");
            }
        }

        private bool isMainPlayer;

        public EventHandler<PropertyValueChangedEventArgs<bool>> IsMainPlayerChanged;

        public static void OnIsMainPlayerChanged(object sender, PropertyValueChangedEventArgs<bool> e)
        {
            var obj = sender as PlayerData;

            if (obj.IsMainPlayerChanged != null)
                obj.IsMainPlayerChanged(obj, e);

            obj.ResetScale();
        }

        #endregion

        #region Scale Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public double Scale
        {
            get { return scale; }
            set
            {
                if (AreEqualObjects(scale, value))
                    return;

                var _fieldOldValue = scale;

                scale = value;

                PlayerData.OnScaleChanged(this, new PropertyValueChangedEventArgs<double>(_fieldOldValue, value));

                this.OnPropertyChanged("Scale");
            }
        }

        private double scale;

        public EventHandler<PropertyValueChangedEventArgs<double>> ScaleChanged;

        public static void OnScaleChanged(object sender, PropertyValueChangedEventArgs<double> e)
        {
            var obj = sender as PlayerData;

            if (obj.ScaleChanged != null)
                obj.ScaleChanged(obj, e);


        }

        #endregion

        #region IsHighlightedPlayer Property and field

        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public bool IsHighlightedPlayer
        {
            get { return isHighlightedPlayer; }
            set
            {
                if (AreEqualObjects(isHighlightedPlayer, value))
                    return;

                var _fieldOldValue = isHighlightedPlayer;

                isHighlightedPlayer = value;

                PlayerData.OnIsHighlightedPlayerChanged(this, new PropertyValueChangedEventArgs<bool>(_fieldOldValue, value));

                this.OnPropertyChanged("IsHighlightedPlayer");
            }
        }

        private bool isHighlightedPlayer;

        public EventHandler<PropertyValueChangedEventArgs<bool>> IsHighlightedPlayerChanged;

        public static void OnIsHighlightedPlayerChanged(object sender, PropertyValueChangedEventArgs<bool> e)
        {
            var obj = sender as PlayerData;

            if (obj.IsHighlightedPlayerChanged != null)
                obj.IsHighlightedPlayerChanged(obj, e);

            obj.ResetScale();
        }

        #endregion

        #region IsFocused Property and field

        /// <summary>
        /// Gets or sets a value indicating whether is aimbot focused on this player or not.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is focused; otherwise, <c>false</c>.
        /// </value>
        [Obfuscation(Exclude = true, ApplyToMembers = false)]
        public bool IsFocused
        {
            get { return isFocused; }
            set
            {
                if (AreEqualObjects(isFocused, value))
                    return;

                var _fieldOldValue = isFocused;

                isFocused = value;

                PlayerData.OnIsFocusedChanged(this, new PropertyValueChangedEventArgs<bool>(_fieldOldValue, value));

                this.OnPropertyChanged("IsFocused");
            }
        }

        private bool isFocused;

        public EventHandler<PropertyValueChangedEventArgs<bool>> IsFocusedChanged;

        public static void OnIsFocusedChanged(object sender, PropertyValueChangedEventArgs<bool> e)
        {
            var obj = sender as PlayerData;

            if (obj.IsFocusedChanged != null)
                obj.IsFocusedChanged(obj, e);


        }

        #endregion


        /// <summary>
        /// Compares last time position setted with a threshold to determine position is valid or not.
        /// </summary>
        /// <returns></returns>
        public bool HaveValidPosition()
        {
            return (DateTime.Now - LastPositionSet).TotalMilliseconds < 250;
        }

        private void ResetScale()
        {
            if (isMainPlayer)
            {
                Scale = 2.0;
                return;
            }

            if (isHighlightedPlayer)
            {
                Scale = 1.5;
                return;
            }


            Scale = 1;
        }

        public override string ToString()
        {
            var str = string.Format("{0}", name);

            return str;
        }
    }
}
